/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.sf.fc.io.ch;

import net.sf.fc.io.event.FileCopyEvent.CopyStatus;
import net.sf.fc.io.event.FileCopyEvent.Level;

import java.io.File;
import java.util.Calendar;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.SimpleTimeZone;

/**
 *
 * Class that extends {@link RenameFileCopyHelper}. This class can be passed
 * to {@link org.apache.commons.io.FileCopier} to rename files that exist.
 * <p>
 * <tt>RenameAppendTimestampFileCopyHelper's</tt> implementation of
 * <tt>handleExistingFile</tt> takes the existing destination file and uses its
 * last modified date to create a String to append to its name. It then tries to
 * rename the existing file on disk and returns the status of the rename
 * attempt.
 *
 * @author David Armstrong
 */
public class RenameAppendTimestampFileCopyHelper extends RenameFileCopyHelper {

    private final String pattern;
    private final DateFormat dateFormat;
    private final Calendar cal;

    /**
     * The default pattern to use when converting the last modified date of
     * the destination file to a String which will be appended to the destination
     * file's name.
     */
    public static final String defaultPattern = ".yyyyMMdd_HHmmss";

    /**
     * The default constructor. Uses the default pattern to convert the last
     * modified date of the destination file to a String.
     */
    public RenameAppendTimestampFileCopyHelper() {
        super();
        pattern = defaultPattern;
        dateFormat = new SimpleDateFormat(pattern);
        cal = Calendar.getInstance();
    }

    /**
     * Constructor that takes a pattern String which will be used to convert the
     * last modified date of the destination file to a String.
     * @param pattern The pattern String to use.
     * @exception NullPointerException if the given pattern is null
     * @exception IllegalArgumentException if the given pattern is invalid
     */
    public RenameAppendTimestampFileCopyHelper(String pattern) {
        super();
        if(pattern == null) {
            throw new NullPointerException("Pattern must not be null");
        }
        this.pattern = pattern;
        dateFormat = new SimpleDateFormat(pattern);
        cal = Calendar.getInstance();
    }

    /**
     * Constructor that takes a boolean value that represents whether or not to
     * use Greenwhich Median Time to represent the last modified date. The
     * default is false (to use local time).
     * @param useGMT true to use GMT, false otherwise
     */
    public RenameAppendTimestampFileCopyHelper(boolean useGMT) {
        super();
        pattern = defaultPattern;
        dateFormat = new SimpleDateFormat(pattern);
        cal = !useGMT ? Calendar.getInstance() : Calendar.getInstance(new SimpleTimeZone(0, "GMT"));
    }

    /**
     * Constructor that takes a pattern String which will be used to convert the
     * last modified date of the destination file to a String and a boolean value
     * that represents whether or not to use Greenwhich Median Time to represent
     * the last modified date. The default is false (to use local time).
     * @param pattern The pattern String to use.
     * @param useGMT true to use GMT, false otherwise
     * @exception NullPointerException if the given pattern is null
     * @exception IllegalArgumentException if the given pattern is invalid
     */
    public RenameAppendTimestampFileCopyHelper(String pattern, boolean useGMT) {
        super();
        if(pattern == null) {
            throw new NullPointerException("Pattern must not be null");
        }
        this.pattern = pattern;
        dateFormat = new SimpleDateFormat(pattern);
        cal = !useGMT ? Calendar.getInstance() : Calendar.getInstance(new SimpleTimeZone(0, "GMT"));
    }

    /**
     * Method that handles destination files that exist during a copy operation.
     * This implementation renames the existing file using the last modified
     * date of the destination file to create a String to be appended to the
     * file name.
     *
     * @param dest the destination file, which exists.
     * @param source the source file.
     * @return Action.CONTINUE to signal to <tt>FileCopier</tt> to move forward with the
     * copy, false to signal that it should not.
     */
    @Override
    public Action handleExistingFile(final File source, final File dest) {

        Action action = Action.CONTINUE;

        File newFile = createNewFile(dest);
        boolean renamed = dest.renameTo(newFile);
        if(renamed) {
            reportCopyEvent(dest, newFile, CopyStatus.RENAME_COMPLETE, Level.INFO, newFile.length());

        } else {
            reportCopyEvent(dest, newFile, CopyStatus.RENAME_FAILED, Level.ERROR, newFile.length());
            action = Action.ABEND;
        }
        return action;
    }

    /**
     * This method renames the destination file using the last modified
     * date of the destination file to create a String to be appended to the
     * file name.
     *
     * @param dest
     * @return a File object with the new name. This method does not rename the
     * file itself, only the File object. The <tt>handleExistingFile</tt> method
     * will rename the file on disk.
     */
    public File createNewFile(final File dest) {
        cal.setTimeInMillis(dest.lastModified());
        return new File(dest.getParent(), dest.getName() + dateFormat.format(cal.getTime()));
    }

    /**
     * This method returns true if subsequent copy operations should be abandoned.
     * Ths method is called by <tt>FileCopier</tt> at the end of each copy operation.
     * This implementation always returns false.
     * @return true if subsequent copy operations should be cancelled.
     */
    @Override
    public boolean cancel() { return false; }
}
